"""
认证工具函数
提供密码加密、JWT Token 生成和验证
"""
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt  # type: ignore
from passlib.context import CryptContext  # type: ignore
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# JWT 配置
# 生产环境请使用环境变量
SECRET_KEY = "4f8d2e7c9b1a3e5f0d8c7b6a5e4f3d2c1a0b9e8f7d6c5b4a3e2f1d0c9b8a7e6f"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30  # 30分钟
REFRESH_TOKEN_EXPIRE_DAYS = 7  # 7天


def verify_password(plain_password: str, hashed_password: str) -> bool:
    """
    验证密码

    Args:
        plain_password: 明文密码
        hashed_password: 加密后的密码

    Returns:
        是否匹配
    """
    return pwd_context.verify(plain_password, hashed_password)


def get_password_hash(password: str) -> str:
    """
    密码加密

    Args:
        password: 明文密码

    Returns:
        加密后的密码
    """
    return pwd_context.hash(password)


def create_access_token(
    data: dict,
    expires_delta: Optional[timedelta] = None
) -> str:
    """
    创建 JWT Access Token

    Args:
        data: 要编码的数据（通常包含用户 ID、用户名等）
        expires_delta: 过期时间增量

    Returns:
        JWT Token 字符串
    """
    to_encode = data.copy()

    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(
            minutes=ACCESS_TOKEN_EXPIRE_MINUTES
        )

    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def create_refresh_token(data: dict) -> str:
    """
    创建 JWT Refresh Token

    Args:
        data: 要编码的数据（通常包含用户 ID、用户名等）

    Returns:
        JWT Refresh Token 字符串
    """
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
    to_encode.update({"exp": expire, "type": "refresh"})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def verify_token(token: str, token_type: str = "access") -> Optional[dict]:
    """
    验证并解析 JWT Token

    Args:
        token: JWT Token 字符串
        token_type: Token 类型 ("access" 或 "refresh")

    Returns:
        解析后的数据，如果验证失败则返回 None
    """
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        # 验证 token 类型
        if token_type == "refresh" and payload.get("type") != "refresh":
            return None
        return payload
    except JWTError:
        return None


# HTTPBearer 安全方案
security = HTTPBearer()


async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(security)
):
    """
    获取当前登录用户（依赖注入）

    从请求头中提取并验证 JWT Token

    Args:
        credentials: HTTP Bearer Token

    Returns:
        当前用户的 User 模型实例

    Raises:
        HTTPException: Token 无效或已过期
    """
    from models.m import User

    token = credentials.credentials
    payload = verify_token(token, token_type="access")

    if payload is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token 无效或已过期",
            headers={"WWW-Authenticate": "Bearer"},
        )

    # 从数据库中获取用户对象
    user_id = payload.get("sub")  # 注意：Token 中使用的是 "sub" 字段
    if not user_id:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token 中缺少用户信息",
            headers={"WWW-Authenticate": "Bearer"},
        )

    user = await User.filter(id=int(user_id)).first()  # user_id 是字符串，需要转换为整数
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户不存在",
            headers={"WWW-Authenticate": "Bearer"},
        )

    return user
